package com.sy.simpleegg.serialport.coincontroller

import android.util.Log
import com.sy.simpleegg.ext.logFile
import com.sy.simpleegg.serialport.base.BaseController
import com.sy.simpleegg.serialport.bean.ComData
import com.sy.simpleegg.serialport.util.CRC16
import com.sy.simpleegg.serialport.util.ConvertHex
import com.sy.simpleegg.util.ByteUtil
import org.cnbleu.serialport.SerialPort
import java.text.SimpleDateFormat
import java.util.Date
import kotlin.random.Random

abstract class BaseCoinController(
    portName: String,
    baudrate: SerialPort.BAUDRATE,
    stopb: SerialPort.STOPB,
    datab: SerialPort.DATAB,
    parity: SerialPort.PARITY,
    private var verNumber: Int,
    speed: Long?
) : BaseController(portName, baudrate, stopb, datab, parity, speed) {

    private val TAG = javaClass.simpleName

    private val head = byteArrayOf(0xAA.toByte())
    private val end = byteArrayOf(0xAB.toByte())

    private val basicLen = 20

    private var tempHex = ""

    private val FLAG_CHECK = "0100"
    private val FLAG_OUT_SEND = "0300"
    private val FLAG_OUT_RESP = "0400"
    private val FLAG_DISPENSER_SEND = "0500"
    private val FLAG_DISPENSER_RESP = "0600"

    override fun onDataReceived(comData: ComData) {
        try {
            val dataHex = ConvertHex.byte2hex(comData.data)
            sticky(dataHex)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("接受数据异常：${e.message}")
        }
    }

    private fun sticky(dataHex: String){
        try {
            logFile("sticky数据：$dataHex")
            if (tempHex.isEmpty()){
                if (dataHex.startsWith("AA") && dataHex.endsWith("AB") && dataHex.length >= basicLen){

                    if (dataHex.contains("ABAA")){
                        val correctDataHex = dataHex.substring(0, dataHex.indexOf("ABAA") + 2)
                        val remainDataHex = dataHex.substring(dataHex.indexOf("ABAA") + 2)
                        if (verifyDataCompleted(correctDataHex)){
                            dataHandler(correctDataHex)
                            if (remainDataHex.isNotEmpty()) {
                                sticky(remainDataHex)
                            }
                        }else{
                            tempHex = correctDataHex
                            if (remainDataHex.isNotEmpty()) {
                                sticky(remainDataHex)
                            }
                        }
                    }else{
                        //完整数据帧
                        tempHex = ""
                        dataHandler(dataHex)
                    }
                }else if (dataHex.startsWith("AA")){
                    //数据帧前半段
                    tempHex = dataHex
                }else if (dataHex.startsWith("AA") && dataHex.contains("ABAA")) {
                    val correctDataHex = dataHex.substring(0, dataHex.indexOf("ABAA") + 2)
                    val remainDataHex = dataHex.substring(dataHex.indexOf("ABAA") + 2)
                    if (verifyDataCompleted(correctDataHex)){
                        dataHandler(correctDataHex)
                        if (remainDataHex.isNotEmpty()) {
                            sticky(remainDataHex)
                        }
                    }else{
                        tempHex = correctDataHex
                        if (remainDataHex.isNotEmpty()) {
                            sticky(remainDataHex)
                        }
                    }
                }else{
                    logFile("收到无效数据：$dataHex")
                }
            }else{

                if (dataHex == "AB" || dataHex == "ABAA"){
                    val temp = tempHex + "AB"
                    if (verifyDataCompleted(temp)){
                        tempHex = ""
                        dataHandler(temp)
                    }else{
                        tempHex += dataHex
                    }
                }
                else {
                    if (dataHex.indexOf("AB") == -1){
                        tempHex += dataHex
                    }else{
                        var tempDataHex = dataHex
                        do {
                            val indexSub = tempDataHex.indexOf("AB")
                            val subHex = tempDataHex.substring(0, indexSub + 2)
                            tempDataHex = tempDataHex.substring(indexSub + 2)
                            tempHex += subHex
                            if (verifyDataCompleted(tempHex)){
                                val resultHex = tempHex
                                tempHex = ""
                                dataHandler(resultHex)
                                if (tempDataHex.isNotEmpty()) {
                                    sticky(tempDataHex)
                                }
                            }else{
                                logFile("粘包数据：检验失败")
                            }
                        }while (tempDataHex.indexOf("AB") != -1)
                    }
                }
            }
        }catch (e: Exception){
            e.printStackTrace()
            logFile("处理数据粘包异常：${e.message}")
        }
    }

    private fun dataHandler(dataHex: String){
        logFile("完整数据帧：$dataHex")
        val dataArea = dataHex.substring(14, dataHex.length - 6)
        when (dataHex.substring(4, 8)){
            FLAG_CHECK -> {
                val devStatus = dataArea.substring(0, 2)
                val motor1Status = dataArea.substring(2, 4)
                val motor2Status = dataArea.substring(4, 6)
                onCheckedResp(devStatus, motor1Status, motor2Status)
            }
            FLAG_OUT_SEND -> {
                val sn = dataArea.substring(0, 64)
                val motor1NeedOutCoins = reverseHex(dataArea.substring(64, 72))
                val motor2NeedOutCoins = reverseHex(dataArea.substring(72, 80))
                val snStatus = dataArea.substring(80, 82)
                val errCode = reverseHex(dataArea.substring(82, 86))
                onOutRec(sn, motor1NeedOutCoins, motor2NeedOutCoins, snStatus, errCode)
            }
            FLAG_OUT_RESP -> {
                val snStatus = dataArea.substring(0, 2)
                val motor1Status = dataArea.substring(2, 4)
                val motor1NeedOutCoins = reverseHex(dataArea.substring(4, 12))
                val motor1RealOutCoins = reverseHex(dataArea.substring(12, 20))
                val motor2Status = dataArea.substring(20, 22)
                val motor2NeedOutCoins = reverseHex(dataArea.substring(22, 30))
                val motor2RealOutCoins = reverseHex(dataArea.substring(30, 38))
                onOutResp(
                    snStatus,
                    motor1Status, motor1NeedOutCoins, motor1RealOutCoins,
                    motor2Status, motor2NeedOutCoins, motor2RealOutCoins)
            }
            FLAG_DISPENSER_SEND -> {
                val dispenser1Enable = dataArea.substring(4, 6)
                val dispenser2Enable = dataArea.substring(6, 8)
                onDispenserRec(dispenser1Enable, dispenser2Enable)
            }
            FLAG_DISPENSER_RESP -> {
                val dispenser1Coins = reverseHex(dataArea.substring(0, 8))
                val dispenser2Coins = reverseHex(dataArea.substring(8, 16))
                onDispenserResp(dispenser1Coins, dispenser2Coins)
            }
        }
    }

    private fun verifyDataCompleted(dataHex: String): Boolean{
        return try {
            dataHex.run {
                if (isEmpty()){
                    return@run false
                }
                if (length < basicLen){
                    logFile("长度不匹配")
                    return@run false
                }
                if (!startsWith("AA")){
                    logFile("起始不为AA")
                    return@run false
                }
                if (!endsWith("AB")){
                    logFile("结尾不为AB")
                    return@run false
                }
                val lenHex = substring(10, 14)
                val len = (lenHex.substring(2) + lenHex.substring(0, 2)).toInt(16)
                val dataLength = (len*2) + 20
                if (dataHex.length != dataLength){
                    logFile("长度不匹配2 len=$len dataLength=$dataLength")
                    return@run false
                }
                val crcHex = dataHex.substring(dataHex.length - 6, dataHex.length - 2)
                val checkHex = dataHex.substring(2, dataHex.length - 6)
                val verifyCrcHex = ByteUtil.bytes2HexStr(CRC16.crc16Checkout(ByteUtil.hexStr2bytes(checkHex)))
                logFile("数据校验位=$crcHex 计算校验位=$verifyCrcHex")
                crcHex == verifyCrcHex
            }
        }catch (e: Exception){
            e.printStackTrace()
            logFile("校验数据完整性异常：${e.message}")
            false
        }
    }

    /**
     * 投币器数据
     * @param dispenser1Coins 投币器1投币数（200毫秒内的投币数据，200ms内投币10个就上报10个，200ms内投币1个就上报1个）
     * @param dispenser2Coins 投币器2投币数（200毫秒内的投币数据，200ms内投币10个就上报10个，200ms内投币1个就上报1个）
     */
    abstract fun onDispenserResp(dispenser1Coins: String, dispenser2Coins: String)

    /**
     * 投币器回复
     * @param dispenser1Enable 投币器1开关 0-关 1-开
     * @param dispenser2Enable 投币器2开关 0-关 1-开
     */
    abstract fun onDispenserRec(dispenser1Enable: String, dispenser2Enable: String)

    /**
     * 出币数据
     * @param snStatus 订单状态
     * @param motor1Status 马达1状态
     * @param motor1NeedOutCoins 马达1应出币数
     * @param motor1RealOutCoins 马达1实际出币数
     * @param motor2Status 马达2状态
     * @param motor2NeedOutCoins 马达2应出币数
     * @param motor2RealOutCoins 马达2实际出币数
     */
    abstract fun onOutResp(
        snStatus: String,
        motor1Status: String,
        motor1NeedOutCoins: String,
        motor1RealOutCoins: String,
        motor2Status: String,
        motor2NeedOutCoins: String,
        motor2RealOutCoins: String
    )

    /**
     * 下单出币回复
     * @param sn 订单号
     * @param motor1NeedOutCoins 马达1应出币数
     * @param motor2NeedOutCoins 马达2应出币数
     * @param snStatus 订单状态
     * @param errCode 异常代码
     */
    abstract fun onOutRec(
        sn: String,
        motor1NeedOutCoins: String,
        motor2NeedOutCoins: String,
        snStatus: String,
        errCode: String
    )

    /**
     * 查询响应数据
     * @param devStatus 设备状态
     * @param motor1Status 马达1状态
     * @param motor2Status 马达2状态
     */
    abstract fun onCheckedResp(devStatus: String, motor1Status: String, motor2Status: String)

    /**
     * 发送数据
     */
    private fun sendByte(dataBytes: ByteArray){
        try {
            val crcCode = CRC16.crc16Checkout(dataBytes)
            val result = ByteUtil.combineBytes(head, dataBytes, crcCode, end)
            sendData(result, 0)
        }catch (e: Exception){
            logFile("sendByte 发送指令异常：${e.message}")
        }
    }

    /**
     * 出币指令 type=1 马达1出币 type=2 马达2出币 count 出币数量
     * @param orderNumber 订单号
     * @param amount 金额
     * @param coins1 马达1出币数
     * @param coins2 马达2出币数
     */
    protected fun outCoin(orderNumberHex: String, amount: Long, coins1: Long, coins2: Long){
        try {
            //随机数
            val random = byteArrayOf(getRandom().toByte())
            //命令号
            val flag = byteArrayOf(0x03, 0x00)
            //版本号
            val ver = byteArrayOf(verNumber.toByte())
            //长度
            val len = byteArrayOf(0x34, 0x00)
            //数据域
//            val uuid = UUID.randomUUID().toString()
//            var snHex = uuid.replace("-", "").trim()
            var snHex = orderNumberHex
            while (snHex.length < 64){
                snHex += "0"
            }
            if (snHex.length > 64){
                snHex = snHex.substring(0, 64)
            }
//            val snHex = createSn(unRepeatSixCode())
            val sn = ByteUtil.hexStr2bytes(snHex)
            val a = ByteUtil.hexStr2bytes(ByteUtil.long2HexStr(amount, 8, true))
            val c1 = ByteUtil.hexStr2bytes(ByteUtil.long2HexStr(coins1, 8, true))
            val c2 = ByteUtil.hexStr2bytes(ByteUtil.long2HexStr(coins2, 8, true))
            val keep = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
            val data = ByteUtil.combineBytes(sn, a, c1, c2, keep)
            val cmdNoCrc = ByteUtil.combineBytes(random, flag, ver, len, data)
            sendByte(cmdNoCrc)
        }catch (e: Exception){
            e.printStackTrace()
            logFile("")
        }
    }

    /**
     * 投币器开关
     * @param dispenser1 投币器1 0-关 1-开
     * @param dispenser2 投币器2 0-关 1-开
     */
    private fun saveController(dispenser1: Int, dispenser2: Int){
        //随机数
        val random = byteArrayOf(getRandom().toByte())
        //命令号
        val flag = byteArrayOf(0x05, 0x00)
        //版本号
        val ver = byteArrayOf(verNumber.toByte())
        //长度
        val len = byteArrayOf(0x0C.toByte(), 0x00)
        //数据域
        val data = byteArrayOf(0x0A.toByte(), 0x1B.toByte(), dispenser1.toByte(), dispenser2.toByte(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)

        val cmdNoCrc = ByteUtil.combineBytes(random, flag, ver, len, data)
        sendByte(cmdNoCrc)
    }

    /**
     * 投币器开关
     * @param enable true-打开 false-关闭
     */
    fun saveEnable(enable: Boolean){
        saveController(if (enable) 1 else 0, if (enable) 1 else 0)
    }

    fun dispenser1Enable(enable: Boolean){
        saveController(if (enable) 1 else 0, 0)
    }

    fun dispenser2Enable(enable: Boolean){
        saveController(0, if (enable) 1 else 0)
    }

    /**
     * 查询控制板状态，当作心跳用
     */
    fun checkState(){
        val random = byteArrayOf(getRandom().toByte())
        val flag = byteArrayOf(0x01, 0x00)
        val ver = byteArrayOf(verNumber.toByte())
        val len = byteArrayOf(0x02, 0x00)
        val data = byteArrayOf(0x02, 0x0A.toByte())
        val cmdNoCrc = ByteUtil.combineBytes(random, flag, ver, len, data)
        sendByte(cmdNoCrc)
    }

    private fun getRandom(): Int{
        return Random.nextInt(256)
    }

    private fun createSn(sn: String): String{
        return try {
            var tempSn = ByteUtil.str2HexString(sn)
            while (tempSn.length < 64){
                tempSn = "0$tempSn"
            }
            tempSn
        }catch (e: Exception){
            e.printStackTrace()
            logFile("生成货道订单号异常：${e.message}")
            var result = "0"
            while (result.length < 64){
                result = "0$result"
            }
            result
        }
    }

    fun unRepeatSixCode(): String {
        return try {
            var sixChar = ""
            val sdf = SimpleDateFormat("yyMMddHHmmss")
            val date = Date()
            val time = sdf.format(date)
            for (i in 0 until time.length / 2) {
                var singleChar: String
                val x = time.substring(i * 2, (i + 1) * 2)
                val b = x.toInt()
                singleChar = if (b < 10) {
                    Integer.toHexString(x.toInt())
                } else if (b >= 10 && b < 36) {
                    (x.toInt() + 55).toChar().toString()
                } else {
                    (x.toInt() + 61).toChar().toString()
                }
                sixChar = sixChar + singleChar
            }
            Log.d(javaClass.simpleName, "生成一个6位不可重复的字符编码是：$sixChar")
            sixChar
        }catch (e: Exception){
            e.printStackTrace()
            "aaaaaa"
        }
    }

    private fun reverseHex(hexStr: String): String{
        return try {
            if (hexStr.isEmpty()){
                return ""
            }
            var tempHex = hexStr
            while (tempHex.length%2 != 0){
                tempHex += "0"
            }
            var result = ""
            for (i in tempHex.indices step 2){
                result = tempHex.substring(i, i + 2) + result
            }
            result
        }catch (e: Exception){
            e.printStackTrace()
            hexStr
        }
    }

}